import { HttpClient } from '@angular/common/http';
import { Inject, Injectable, Optional } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, Router, RouterStateSnapshot, UrlTree } from '@angular/router';
import { CookieService } from 'ngx-cookie-service';
import { map } from 'rxjs/operators';
import { SecurityConfig, SECURITY_CONFIG } from './security-init.service';

@Injectable({
  providedIn: 'root'
})
export class UserAuthService implements CanActivate, CanActivateChild {

  constructor(
    private http: HttpClient, private router: Router, private cookieService: CookieService,
    @Optional() @Inject(SECURITY_CONFIG) private config: SecurityConfig) {

  }


  private user: LoginUser;

  login(authDatas: any, success: (res: any) => void, error?: () => void): void {
    this.http.post(this.config.loginApi, authDatas, { withCredentials: true }).pipe(map(res => res as any)).subscribe({
      next: res => {
        if (res.status === 'success') {
          res.user.token = res.token;
          sessionStorage.setItem(this.config.loginUser ?? 'LOGIN_USER', JSON.stringify(res.user));
          // this.router.navigate([this.config.authLoginedRouter ?? '/root']);
          this.cookieService.set(this.config.userLoginName ?? 'USER_LOGIN_NAME',
            res.token, undefined, this.config.scope ?? '/', this.config.domain);

          success(res);
        } else {
          error();
        }
      },
      error
    });
  }

  logout(): void {
    localStorage.clear();
    sessionStorage.clear();
    this.cookieService.deleteAll(this.config.scope ?? '/', this.config.domain);
  }

  async getUser(): Promise<LoginUser> {
    if (this.cookieService.get(this.config.userLoginName ?? 'USER_LOGIN_NAME')) {
      let user: LoginUser = JSON.parse(sessionStorage.getItem(this.config.loginUser ?? 'LOGIN_USER'));
      if (user) {
        return new Promise((res, rej) => {
          res(user);
        });
      } else {
        try {
          user = await this.http.get(this.config.authApi, { withCredentials: true }).toPromise();
        } catch (e) {
          return new Promise((res, rej) => {
            rej(null);
          });
        }
      }
      return new Promise((res, rej) => {
        res(user.user);
      });
    }else{
      return new Promise((res, rej) => {
        res(null);
      });
    }
  }

  async authUser(): Promise<boolean | UrlTree> {
    let user: any;
    let toUrl: boolean | UrlTree = true;
    const token = this.cookieService.get(this.config.userLoginName ?? 'USER_LOGIN_NAME');
    if (token) {
      try {
        user = sessionStorage.getItem(this.config.loginUser ?? 'LOGIN_USER');
        if (user && user.token === token) {
          return new Promise((res, rej) => {
            res(toUrl);
          });
        } else {
          user = await this.http.get(this.config.authApi, { withCredentials: true }).toPromise();
          user = user.user;
          user.token = token;
        }

      } catch (ex) {
        user = await this.http.get(this.config.authApi, { withCredentials: true }).toPromise();
        user = user.user;
      }
      if (user) {
        sessionStorage.setItem(this.config.loginUser ?? 'LOGIN_USER', JSON.stringify(user));
      } else {
        toUrl = this.router.parseUrl(this.config.authFailRouter ?? `/cantauth`);
      }
      return new Promise((res, rej) => {
        res(toUrl);
      });
    } else {
      return new Promise((res, rej) => {
        res(this.router.parseUrl(this.config.authFailRouter ?? `/cantauth`));
      });
    }
  }

  async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot)
    : Promise<boolean | UrlTree> {
    const checked = this.authUser();
    const user: LoginUser = this.getUser();
    if (route.data) {

    }
    return checked;
  }
  async canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot)
    : Promise<boolean | UrlTree> {
    return this.authUser();
  }
}

export interface LoginUser {
  id?: string;
  code?: string;
  name?: string;
  token?: string;
  [property: string]: any;
}
